package pushengine;

import android.media.AudioRecord;
import android.media.audiofx.AcousticEchoCanceler;
import android.media.audiofx.AutomaticGainControl;
import android.media.projection.MediaProjection;
import android.os.Message;
import android.util.Log;

import com.github.faucamp.simplertmp.RtmpHandler;
import com.seu.magicfilter.utils.MagicFilterType;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.SocketException;
import android.os.Handler;

/**
 * Created by Leo Ma on 2016/7/25.
 */
public class YuerPublisher implements RtmpHandler.RtmpListener, YuerEncodeHandler.YuerEncodeListener{

    private static AudioRecord mic;
    private static AcousticEchoCanceler aec;
    private static AutomaticGainControl agc;
    private byte[] mPcmBuffer = new byte[4096];
    private Thread aworker;

    private YuerCameraView mCameraView;

    private boolean sendAudioOnly = false;
    private int videoFrameCount;
    private long lastTimeMillis;
    private double mSamplingFps;

    private SrsFlvMuxer mFlvMuxer;
    private SrsEncoder mEncoder;
	protected String TAG = "SrsPublisher";
    private boolean isRecennecting = false;

    private static int reConnectCount;
    private static final int reconnectThreshold = 20;
    private String mRtmpUrl;
	private WeakReference<YuerPublisherListener> mWeakListener;
    private static final int MSG_RTMP_CONNECTED = 0x1;
	private static final int MSG_RTMP_DISCONNECTED = 0x2;
	private static final int MSG_EXCEPTION = 0x3;
	private static final int MSG_ENCODER_EXCEPTION = 0x4;
	private int mPreviewWidth = 1280;
    private int mPreviewHeight = 720;
    private int mEncodeWidth = 720;
    private int mEncodeHeight = 1280;
    private int mVideoBite = 1500*1000;
    private MediaProjection mMediaProjection = null;
    private int mScreenDensity = 480;
    private boolean isMute = false;
    public class YuerVideoEncodeQuality
    {
        public final static int VIDEO_ENCODE_QUALITY_NONE       = 0;
        public final static int VIDEO_ENCODE_QUALITY_LOW       = 1;
        public final static int VIDEO_ENCODE_QUALITY_MIDDLE    = 2;
        public final static int VIDEO_ENCODE_QUALITY_HIGH      = 3;
    }

    public YuerPublisher(int videoEncodeQuality) {
        switch (videoEncodeQuality){
            case YuerVideoEncodeQuality.VIDEO_ENCODE_QUALITY_LOW:
                mEncodeWidth = 480;
                mEncodeHeight = 848;
                mVideoBite = 800 *1024;
                break;
            case YuerVideoEncodeQuality.VIDEO_ENCODE_QUALITY_MIDDLE:
                mEncodeWidth = 544;
                mEncodeHeight = 960;
                mVideoBite = 1000 *1024;
                break;
            case YuerVideoEncodeQuality.VIDEO_ENCODE_QUALITY_HIGH:
                mEncodeWidth = 720;
                mEncodeHeight = 1280;
                mVideoBite = 1500 *1024;
                break;
			default:
				break;
        }
    }
    public void setCameraParameter(YuerCameraView view,int previewWidth, int previewHeight){
        mPreviewWidth = previewWidth;
        mPreviewHeight = previewHeight;
        mCameraView = view;
        if(mCameraView != null){
            mCameraView.setPreviewResolution(mPreviewWidth, mPreviewHeight);
			mCameraView.setPreviewCallback(new YuerCameraView.PreviewCallback() {
				@Override
				public void onGetRgbaFrame(byte[] data, int width, int height) {
					//calcSamplingFps();
					if (!sendAudioOnly) {
						mEncoder.onGetRgbaFrame(data, width, height);
					}
				}
			});
		}
	}

    public void setScreenCaptureParameter(MediaProjection MediaProjection, int ScreenDensity,int videoWidth, int videoHeight){
		mMediaProjection = MediaProjection;
        mScreenDensity = ScreenDensity;
		mEncodeWidth = videoWidth;
		mEncodeHeight = videoHeight;

	}

    private void calcSamplingFps() {
        // Calculate sampling FPS
        if (videoFrameCount == 0) {
            lastTimeMillis = System.nanoTime() / 1000000;
            videoFrameCount++;
        } else {
            if (++videoFrameCount >= SrsEncoder.VGOP) {
                long diffTimeMillis = System.nanoTime() / 1000000 - lastTimeMillis;
                mSamplingFps = (double) videoFrameCount * 1000 / diffTimeMillis;
                videoFrameCount = 0;
            }
        }
    }

    public void startPublish(String rtmpUrl,boolean useCamera) {
		Log.e(TAG, "startpush");
		mRtmpUrl = rtmpUrl;
		if(mFlvMuxer != null || mEncoder != null){
			return;
		}
		mEncoder = new SrsEncoder(new YuerEncodeHandler(this));
		if(mEncoder != null){
			mEncoder.setVideoEncodeParameter(mEncodeWidth,mEncodeHeight,mVideoBite);
			if(!useCamera && mMediaProjection != null){
				mEncoder.setScreenCaptureParameter(mMediaProjection,mScreenDensity);
			}
			mFlvMuxer = new SrsFlvMuxer(new RtmpHandler(this));
			if (mFlvMuxer != null) {
				mEncoder.setFlvMuxer(mFlvMuxer);
				mFlvMuxer.start(rtmpUrl);
				mFlvMuxer.setVideoResolution(mEncodeWidth, mEncodeHeight,mVideoBite);
			}
		}
		startEncode(useCamera);

    }

    public void stopPublish() {
		Log.e(TAG, "stoppush");
		if(mEncoder != null){
            stopEncode();
            mEncoder = null;
		}
        if (mFlvMuxer != null) {
            mFlvMuxer.stop();
			mFlvMuxer = null;
        }
        if(mHandle != null){
            mHandle.removeCallbacksAndMessages(null);
            mHandle = null;
		}
    }
	/*-----------------------------------camera interface -----------------------------------------*/
    public void startCamera() {
        if(mCameraView != null){
            mCameraView.startCamera();
		}
    }

    public void stopCamera() {
        if(mCameraView != null){
			mCameraView.stopCamera();
		}
    }
	
    public int getCamraId() {
        return mCameraView.getCameraId();
    }
	
    public boolean switchCameraFilter(MagicFilterType type) {
        return mCameraView.setFilter(type);
    }

    public void switchCameraFace(int id) {
        mCameraView.stopCamera();
        mCameraView.setCameraId(id);
        if (mEncoder != null && mEncoder.isWorking()) {
            mCameraView.enableEncoding();
        }
        mCameraView.startCamera();
    }
	
	public boolean takePicture(String filePath) {
       boolean mResult = false;
	   if(mCameraView != null){
	   	 mResult = mCameraView.takePicture(filePath);
	   	}
	   return mResult;
    }
	/*-----------------------------------encoder interface -----------------------------------------*/	
    public void startEncode(boolean useCamera) {
        if (!mEncoder.start(useCamera)) {
            return;
        }
		if(mCameraView != null){
            mCameraView.enableEncoding();
		}
        startAudioCapture();
    }

    public void stopEncode() {
        stopAudioCapture();
        stopCamera();
        mEncoder.stop();
    }
	/*-----------------------------------audio Capture  interface -----------------------------------------*/
    private void startAudioCapture() {
        mic = mEncoder.chooseAudioRecord();
        if (mic == null) {
            return;
        }

        if (AcousticEchoCanceler.isAvailable()) {
            aec = AcousticEchoCanceler.create(mic.getAudioSessionId());
            if (aec != null) {
                aec.setEnabled(true);
            }
        }

        if (AutomaticGainControl.isAvailable()) {
            agc = AutomaticGainControl.create(mic.getAudioSessionId());
            if (agc != null) {
                agc.setEnabled(true);
            }
        }

        aworker = new Thread(new Runnable() {
            @Override
            public void run() {
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
                mic.startRecording();
                while (!Thread.interrupted()) {
                    int size = mic.read(mPcmBuffer, 0, mPcmBuffer.length);
                    if (size <= 0) {
                        break;
                    }
					if(!isMute){
						mEncoder.onGetPcmFrame(mPcmBuffer, size);
					}
                }
            }
        });
        aworker.start();
    }
    public void stopAudioCapture() {
        if (aworker != null) {
            aworker.interrupt();
            try {
                aworker.join();
            } catch (InterruptedException e) {
                aworker.interrupt();
            }
            aworker = null;
        }

        if (mic != null) {
            mic.setRecordPositionUpdateListener(null);
            mic.stop();
            mic.release();
            mic = null;
        }

        if (aec != null) {
            aec.setEnabled(false);
            aec.release();
            aec = null;
        }

        if (agc != null) {
            agc.setEnabled(false);
            agc.release();
            agc = null;
        }
    }

    public void setSendAudioOnly(boolean flag) {
        sendAudioOnly = flag;
    }

    public void mute(boolean flag) {
		isMute = flag;
    }
   /*-----------------------------------msg handle interface -----------------------------------------*/	
    Handler mHandle = new Handler() {
	    @Override
	    public void handleMessage(Message msg) {
		    switch (msg.what) {
				case MSG_RTMP_CONNECTED:
					handleConnected();
					break;
			    case MSG_RTMP_DISCONNECTED:
				    handleRtmpDisconnect((boolean) msg.obj);
				    break;
				case MSG_ENCODER_EXCEPTION:
					handleException();
					break;
			    case MSG_EXCEPTION:
                    if(!isRecennecting){
                        handleException();
				   	}
				    break;	   
		    }
	    }
    };
    private void handleConnected() {
        YuerPublisherListener listener = mWeakListener.get();
        if(listener != null){
            listener.onPublishStart();
        }
	    reConnectCount = 0;
	    isRecennecting = false;
	    if(mEncoder != null){
		    mEncoder.setReconnectFlag(false);
	    }
    }

    private void handleException() {
	    YuerPublisherListener listener = mWeakListener.get();
	    if(listener != null){
		    listener.onPublishException();
		 }
    }
    private void handleRtmpDisconnect(boolean needReconnect){
	    Log.e(TAG, "onRtmpDisconnected " + needReconnect + "   " + reConnectCount);
        YuerPublisherListener listener = mWeakListener.get();
        if(listener != null){
            listener.onPublishStop();
        }
	    if (needReconnect) {
		    if (reConnectCount++ < reconnectThreshold) {
			    isRecennecting = true;
                if(mEncoder != null){
                    mEncoder.setReconnectFlag(true);
                }
			    if (mFlvMuxer != null) {
				    mFlvMuxer.stop();
			    }
		    } else {
			    Log.e(TAG, "reconnect faild");
			    reConnectCount = 0;
			    isRecennecting = false;
			    handleException();
		    }
	   } else {	   
	       if (isRecennecting) {
               if (mFlvMuxer != null) {
				   mFlvMuxer.start(mRtmpUrl);
				   mFlvMuxer.setVideoResolution(mEncodeWidth, mEncodeHeight,mVideoBite);
			   }

		   }
	   }
    }
   
   /*----------------------------------- notify  to activity -----------------------------------------*/	
    public interface YuerPublisherListener {
         void onPublishStart();
		 void onPublishStop();
		 void onPublishException();
    }
   
    public void setListener(YuerPublisherListener listener){
	    mWeakListener = new WeakReference<YuerPublisherListener>(listener);
    }
   
    /*-----------------------------------YuerEncodeHandler reciever -----------------------------------------*/

    @Override
    public void onNetworkWeak() {
    }

    @Override
    public void onNetworkResume() {
    }

    @Override
    public void onEncodeIllegalArgumentException(IllegalArgumentException e) {
        Log.e(TAG, "onEncodeIllegalArgumentException");
		if(mHandle != null){
           mHandle.sendEmptyMessage(MSG_ENCODER_EXCEPTION);
		}
    }

   /*----------------------------------- rtmp msg reciever-----------------------------------------*/	
    @Override
    public void onRtmpConnecting(String msg) {
    }

    @Override
    public void onRtmpConnected(String msg) {
        Log.e(TAG, "onRtmpConnected");
		if(mHandle != null){
		    mHandle.sendEmptyMessage(MSG_RTMP_CONNECTED);
		}
    }

    @Override
    public void onRtmpVideoStreaming() {
    }

    @Override
    public void onRtmpAudioStreaming() {
    }

    @Override
    public void onRtmpStopped() {
        Log.e(TAG, "stop");
    }

    @Override
    public void onRtmpDisconnected(boolean needReconnect) {
        Log.e(TAG, "onRtmpDisconnected  " +  needReconnect);
		if(mHandle != null){
            mHandle.obtainMessage(MSG_RTMP_DISCONNECTED,needReconnect).sendToTarget();
		}
    }

    @Override
    public void onRtmpVideoFpsChanged(double fps) {
        Log.i(TAG, String.format("Output Fps: %f", fps));
    }

    @Override
    public void onRtmpVideoBitrateChanged(double bitrate) {
        int rate = (int) bitrate;
        if (rate / 1000 > 0) {
            Log.i(TAG, String.format("Video bitrate: %f kbps", bitrate / 1000));
        } else {
            Log.i(TAG, String.format("Video bitrate: %d bps", rate));
        }
    }

    @Override
    public void onRtmpAudioBitrateChanged(double bitrate) {
        int rate = (int) bitrate;
        if (rate / 1000 > 0) {
            Log.i(TAG, String.format("Audio bitrate: %f kbps", bitrate / 1000));
        } else {
            Log.i(TAG, String.format("Audio bitrate: %d bps", rate));
        }
    }

    @Override
    public void onRtmpSocketException(SocketException e) {
        Log.e(TAG, "onRtmpSocketException msg" + e.getMessage());
    }
    @Override
    public void onRtmpIOException(IOException e) {
        Log.e(TAG, "onRtmpIOException");
    }
    @Override
    public void onRtmpIllegalArgumentException(IllegalArgumentException e) {
        Log.e(TAG, "onRtmpIllegalArgumentException msg"+ e.getMessage());
		if(mHandle != null){
		    mHandle.sendEmptyMessage(MSG_EXCEPTION);
		}
    }
    @Override
    public void onRtmpIllegalStateException(IllegalStateException e) {
        Log.e(TAG, "onRtmpIllegalStateException msg = " + e.getMessage());
		if(mHandle != null){
			mHandle.sendEmptyMessage(MSG_EXCEPTION);
        }
    }
}
